/**
 * @file LogBase.h
 * @author  
 * @brief 日志打印输出的基类，能够输出到控制台及文件中
 * @version 1.0
 * @date 2020-07-24
 * 
 * @copyright Copyright (c) 2020
 * 
 */

#pragma once

#include <sstream>
#include <string>
#include <iostream>
#include <memory>
#include <vector>

namespace behavior_controller
{

    enum class LogLevel //log等级
    {
        debug,
        info,
        warn,
        error
    };

    struct LogContent //log中的元素内容
    {
        std::string name;
        LogLevel severity;
        std::string directory;
        std::string storage_directory;
    };

    /**
     * @brief <<重载
     * 
     * @param strm log的字符串流
     * @param level log等级
     * @return std::ostream& 
     */
    inline std::ostream &operator<<(std::ostream &strm, LogLevel level)
    {
        static const char *strings[] = {"debug", "info", "warn", "error"};

        size_t lv = static_cast<std::size_t>(level);
        if (lv < sizeof(strings) / sizeof(*strings))
            strm << strings[lv];
        else
            strm << lv;

        return strm;
    }

    /**
     * @brief log类型的基类
     * 
     */
    class LogBase
    {
    public:
        LogBase(std::string name, std::string type, LogLevel lvl)
            : name_(name), type_(type), level_(lvl)
        {
        }
        virtual ~LogBase() = default;

        template <typename... Args>
        void Error(const std::string &content) const
        {
            if (level_ > LogLevel::error)
                return;
            ErrorLog(content);
        }

        template <typename... Args>
        void Warn(const std::string &content) const
        {
            if (level_ > LogLevel::warn)
                return;
            WarnLog(content);
        }

        template <typename... Args>
        void Info(const std::string &content) const
        {
            if (level_ > LogLevel::info)
                return;
            InfoLog(content);
        }

        template <typename... Args>
        void Debug(const std::string &content) const
        {
            if (level_ > LogLevel::debug)
                return;
            DebugLog(content);
        }

        virtual void Flush() const = 0;

        const std::string &name() const { return name_; }
        const std::string &type() const { return type_; }
        const LogLevel &level() const { return level_; }

    private:
        virtual void ErrorLog(const std::string &log) const = 0;
        virtual void WarnLog(const std::string &log) const = 0;
        virtual void InfoLog(const std::string &log) const = 0;
        virtual void DebugLog(const std::string &log) const = 0;

        std::string name_;
        std::string type_;
        LogLevel level_;
    };

    /**
     * @brief 用来加载控制台log、文件log的容器
     * 
     */
    class LogSet
    {
    public:
        void AddLog(const std::shared_ptr<LogBase> &log_ptr)
        {
            log_ptrs_.push_back(log_ptr);
        }

        template <typename... Args>
        void Error(Args &&... args) const
        {
            if (level_ > LogLevel::error)
                return;
            std::stringstream sstrm;
            GetLogString(sstrm, args...);
            std::string content = sstrm.str();
            for (size_t i = 0; i < log_ptrs_.size(); i++)
            {
                log_ptrs_[i]->Error(content);
            }
        }

        template <typename... Args>
        void Warn(Args &&... args) const
        {
            if (level_ > LogLevel::warn)
                return;
            std::stringstream sstrm;
            GetLogString(sstrm, args...);
            std::string content = sstrm.str();
            for (size_t i = 0; i < log_ptrs_.size(); i++)
            {
                log_ptrs_[i]->Warn(content);
            }
        }

        template <typename... Args>
        void Info(Args &&... args) const
        {
            if (level_ > LogLevel::info)
                return;
            std::stringstream sstrm;
            GetLogString(sstrm, args...);
            std::string content = sstrm.str();
            for (size_t i = 0; i < log_ptrs_.size(); i++)
            {
                //std::cout << "i:" << (int)i << " " << log_ptrs_[i]->type() << " " << log_ptrs_[i]->name() << std::endl;
                log_ptrs_[i]->Info(content);
            }
        }

        template <typename... Args>
        void Debug(Args &&... args) const
        {
            if (level_ > LogLevel::debug)
                return;
            std::stringstream sstrm;
            GetLogString(sstrm, args...);
            std::string content = sstrm.str();
            for (size_t i = 0; i < log_ptrs_.size(); i++)
            {
                log_ptrs_[i]->Debug(content);
            }
        }
        /**
         * @brief 清空log缓存内容
         * 
         */
        void Flush()
        {
            for (size_t i = 0; i < log_ptrs_.size(); i++)
            {
                log_ptrs_[i]->Flush();
            }
        }

    private:
        template <typename FArg, typename... Args>
        /**
         * @brief 获取log字符串
         * 
         */
        void GetLogString(std::ostream &out, FArg &&farg, Args... args) const
        {
            out << farg;
            std::initializer_list<int>{(out << " " << args, 0)...};
        }

        std::vector<std::shared_ptr<LogBase>> log_ptrs_;
        LogLevel level_;
    };

    /**
     * @brief 控制台log输出类
     * 
     */
    class StdoutLogger : public LogBase
    {
    public:
        StdoutLogger(std::string name, LogLevel lvl);

        void Flush() const override;

    private:
        void ErrorLog(const std::string &log) const override;
        void WarnLog(const std::string &log) const override;
        void InfoLog(const std::string &log) const override;
        void DebugLog(const std::string &log) const override;
    };

} // namespace behavior_controller